import { Vec3 } from "/common/lib/math/Vec3.js"
import { Vec2 } from "/common/lib/math/Vec2.js"
/**
 * 定义光源模型
 * 
 * 环境光：单独设置
 * 平行光：光源方向、光源强度
 * 点光源：光源位置、光源强度、衰减系数
 * 聚光灯：光源位置、光源方向、光源角度、光源强度、衰减系数
 * 探照灯：光源位置、光源方向、光源半径、光源强度、衰减系数
 * 
*/
export class Phone{
    constructor(ambientLight = [0.5, 0.5, 0.5]){
        this.ambientLight = {value: ambientLight};
        this.pointLights = new Set();
        this.directionalLights = new Set();
        this.spotLights = new Set();
        this.searchLights = new Set();
    }
    addLight(light){
        const { direction, position, color, decay, angle, radius } = light;
        if(!direction && !position) throw new Error("无效的光源！");
        light.color = color || [1, 1, 1];
        // 没有位置，是平行光
        if(!position){
            this.directionalLights.add(light);
        }else{
            light.decay = decay || [0, 0, 1];
            // 有位置有方向是聚光灯
            if(angle){
                this.spotLights.add(light);
            }else{
                if(radius)
                    this.searchLights.add(light); // 探照灯
                else
                    this.pointLights.add(light);
            }
        }
    }
    removeLight(light){
        if(this.directionalLights.has(light)) this.directionalLights.delete(light);
        else if(this.spotLights.has(light)) this.spotLights.delete(light);
        else if(this.pointLights.has(light)) this.pointLights.delete(light);
        else if(this.searchLights.has(light)) this.searchLights.delete(light);
    }
    get uniforms(){
        // 每种光最多添加16个光源
        const MAX_NUM = 16;
        const uniforms = {};
        // 处理平行光
        uniforms.directionalLightDirection = { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.directionalLightColor =  { value: new Float32Array(MAX_NUM * 3) }; 
        
        [...this.directionalLights].forEach((light, idx) => {
            uniforms.directionalLightDirection.value.set(light.direction, idx * 3);
            uniforms.directionalLightColor.value.set(light.color, idx * 3);
        });

        // 处理点光源
        uniforms.pointLightPosition = { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.pointLightColor =  { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.pointLightDecay =  { value: new Float32Array(MAX_NUM * 3) }; 
        [...this.pointLights].forEach((light, idx) => {
            uniforms.pointLightPosition.value.set(light.position, idx * 3);
            uniforms.pointLightColor.value.set(light.color, idx * 3);
            uniforms.pointLightDecay.value.set(light.decay, idx * 3);
        });

        // 处理聚光灯
        uniforms.spotLightPosition = { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.spotLightDirection = { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.spotLightColor =  { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.spotLightDecay =  { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.spotLightAngle =  { value: new Float32Array(MAX_NUM) }; 
        [...this.spotLights].forEach((light, idx) => {
            uniforms.spotLightPosition.value.set(light.position, idx * 3);
            uniforms.spotLightDirection.value.set(light.direction, idx * 3);
            uniforms.spotLightColor.value.set(light.color, idx * 3);
            uniforms.spotLightDecay.value.set(light.decay, idx * 3);
            uniforms.spotLightAngle.value[idx] = light.angle;
        });

        // 处理探照灯
        uniforms.searchLightPosition = { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.searchLightDirection = { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.searchLightColor =  { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.searchLightDecay =  { value: new Float32Array(MAX_NUM * 3) }; 
        uniforms.searchLightRadius =  { value: new Float32Array(MAX_NUM) }; 
        [...this.searchLights].forEach((light, idx) => {
            uniforms.searchLightPosition.value.set(light.position, idx * 3);
            uniforms.searchLightDirection.value.set(light.direction, idx * 3);
            uniforms.searchLightColor.value.set(light.color, idx * 3);
            uniforms.searchLightDecay.value.set(light.decay, idx * 3);
            uniforms.searchLightRadius.value[idx] = light.radius;
            
        });

        return {
            ambientLight:this.ambientLight,
            ...uniforms
        }
    }
}

/**
 * 切线空间中的切线和副切线计算
 * @param {Geometry} geometry 空间几何体实例对象
 * */ 
 export function createTB(geometry) {
    const {position, index, uv} = geometry.attributes;
    if(!uv) throw new Error('NO uv.');
    function getTBNTriangle(p1, p2, p3, uv1, uv2, uv3) {
        const edge1 = new Vec3().sub(p2, p1);
        const edge2 = new Vec3().sub(p3, p1);
        const deltaUV1 = new Vec2().sub(uv2, uv1);
        const deltaUV2 = new Vec2().sub(uv3, uv1);

        const tang = new Vec3();
        const bitang = new Vec3();

        const f = 1.0 / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);

        tang.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
        tang.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
        tang.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);

        tang.normalize();

        bitang.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x);
        bitang.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y);
        bitang.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);

        bitang.normalize();

        return {tang, bitang};
    }

    const size = position.size;
    if(size < 3) throw new Error('Error dimension.');

    const len = position.data.length / size;
    const tang = new Float32Array(len * 3);
    const bitang = new Float32Array(len * 3);

    const ilen = index ? index.data.length : len;

    for(let i = 0; i < ilen; i += 3) {
        const i1 = index ? index.data[i] : i;
        const i2 = index ? index.data[i + 1] : i + 1;
        const i3 = index ? index.data[i + 2] : i + 2;

        const p1 = [position.data[i1 * size], position.data[i1 * size + 1], position.data[i1 * size + 2]];
        const p2 = [position.data[i2 * size], position.data[i2 * size + 1], position.data[i2 * size + 2]];
        const p3 = [position.data[i3 * size], position.data[i3 * size + 1], position.data[i3 * size + 2]];

        const u1 = [uv.data[i1 * 2], uv.data[i1 * 2 + 1]];
        const u2 = [uv.data[i2 * 2], uv.data[i2 * 2 + 1]];
        const u3 = [uv.data[i3 * 2], uv.data[i3 * 2 + 1]];

        const {tang: t, bitang: b} = getTBNTriangle(p1, p2, p3, u1, u2, u3);
        tang.set(t, i1 * 3);
        tang.set(t, i2 * 3);
        tang.set(t, i3 * 3);
        bitang.set(b, i1 * 3);
        bitang.set(b, i2 * 3);
        bitang.set(b, i3 * 3);
    }
    geometry.addAttribute('tang', { data: tang, size: 3 });
    geometry.addAttribute('bitang', { data: bitang, size: 3 });
    return geometry;
}